/**
 * @file       godemool/bitstream.h
 * @brief      Bit stream buffer.
 * @author     ben "boin" bonoeil
 * @date       2015-02
 */

/* Copyright (c) 2007-2015, ben "boin" bonoeil */

#ifndef GODEMOOL_BITSTREAM_H
#define GODEMOOL_BITSTREAM_H

#include <string>
#include <cstdint>
#include <exception>
#include <cassert>

namespace boin {

  class BitStream {

  public:
    /// Generic overflow/underflow exception.
    struct Flow : std::exception {
      int m_ovf;
      Flow(int n) : m_ovf(n) {};
      operator int() const { return m_ovf; }
      const char * what() { return m_ovf < 0 ? "overflow" : "underflow"; }
    };

    /// Exception when running out of bits.
    struct Underflow : Flow {
      Underflow(int n) : Flow(n) {}
    };

    /// Exception when destination buffer overflows.
    struct Overflow : Flow {
      Overflow(int n) : Flow(-n) {}
    };

  protected:
    int bitcnt, bitpos;
    uint32_t * buf;

    inline uint32_t ZZenc(int32_t n) {
      return(n << 1) ^ (n >> (sizeof(int32_t)*8-1));
    }
    inline int32_t ZZdec(uint32_t n) {
      return(n >> 1) ^ -static_cast<int32_t>(n & 1);
    }
    inline uint64_t ZZenc(int64_t n) {
      return(n << 1) ^ (n >> (sizeof(int64_t)*8-1));
    }
    inline int64_t ZZdec(uint64_t n) {
      return(n >> 1) ^ -static_cast<int64_t>(n & 1);
    }

    unsigned int _Read(int bits);
    template<typename T> T ReadVa();


  public:

    int  Pos() const { return bitpos; }
    void Skip(int bits);
    unsigned int Read1();
    unsigned int Read(int bits);
    int Extend(int bits);
    unsigned int Read8()  { return Read(8);  }
    unsigned int Read16() { return Read(16); }
    unsigned int Read32() { return Read(32); }

    float ReadFP32();
    int  Read(char * s, int max = 0, bool nl = false);
    int  StrLen(int max = 0, bool nl = false) { return Read(nullptr, max, nl); }
    std::string & Read(std::string & str, int max = 0, bool nl = false);

    uint8_t * PosPtr() {
      assert(! (bitpos & 7) );
      return reinterpret_cast<uint8_t *>(buf)+(bitpos>>3);
    }
    operator char *() { return (char *) PosPtr(); }
    operator void *() { return (void *) PosPtr(); }

    int32_t  ReadVaI32();
    int64_t  ReadVaI64();
    uint32_t ReadVaU32();
    uint64_t ReadVaU64();

    int Bits()  const { return  bitcnt; }
    int Bytes() const { return (bitcnt+7) >> 3; }
    int Words() const { return (bitcnt+15) >> 4; }
    int Longs() const { return (bitcnt+31) >> 5; }

    int RemBits()  const { return  bitcnt - bitpos; }
    int RemBytes() const { return (bitcnt - bitpos + 7) >> 3; }
    int RemWords() const { return (bitcnt - bitpos + 15) >> 4; }
    int RemLongs() const { return (bitcnt - bitpos + 31) >> 5; }

    void Set(void * addr, int bytes) {
      bitcnt = bytes << 3;
      bitpos = 0;
      buf = (uint32_t *)addr;
    }

    BitStream()
      : bitcnt(0), bitpos(0), buf(nullptr) {}
    BitStream(void * addr, int bytes) { Set(addr,bytes); }

  };

}

#endif

/* Local Variables:  */
/* mode: c++         */
/* End:              */
